﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using PlusPlusPaint.Layers;

namespace PlusPlusPaint.GUI
{
    class LayerPanelControl : Panel
    {
        #region membervars
        FlowLayoutPanel layoutPanel;
        Layers.LayerManager layerManager;
        List<Tuple<LayerSummary, LayerPanel>> layers = new List<Tuple<LayerSummary,LayerPanel>>();
        LayerPanel selectedLayerPanel;
        List<LayerPanel> selectedLayerPanels;
        Size sketchSize;
        bool ctrlDown = false;
        #endregion

        #region properties
        public LayerPanel SelectedLayerPanel
        {
            get
            {
                return selectedLayerPanel;
            }
            set
            {
                selectedLayerPanel = value;
                if (!ctrlDown)
                    selectedLayerPanels.Clear();
                selectedLayerPanels.Add(value);

                foreach (LayerPanel lp in layoutPanel.Controls)
                    lp.BackColor = selectedLayerPanels.Contains(lp) ? SystemColors.ControlLightLight : SystemColors.Control;
            }
        }

        public IList<Layers.LayerSummary> SelectedLayers
        {
            get
            {
                List<Layers.LayerSummary> layerList = new List<Layers.LayerSummary>();
                foreach (LayerPanel lp in selectedLayerPanels)
                    layerList.Add(lp.LayerSummary);
                return layerList;
            }
        }

        public int IndexOfSelected
        {
            get
            {
                return layers.FindIndex((tpl) => tpl.Item2 == this.selectedLayerPanel);
            }
        }
        public Size SketchSize
        {
            get
            {
                return sketchSize;
            }
            set
            {
                sketchSize = value;
            }
        }

        public bool CtrlDown
        {
            get
            {
                return ctrlDown;
            }
            set
            {
                ctrlDown = value;
            }
        }
        #endregion

        #region constructors
        public LayerPanelControl(Layers.LayerManager lm)
        {
            layoutPanel = new FlowLayoutPanel();
            layoutPanel.WrapContents = false;
            layoutPanel.FlowDirection = FlowDirection.TopDown;
            layoutPanel.AutoScroll = true;
            this.Controls.Add(layoutPanel);

            this.createStatusBar();

            layerManager = lm;
            layerManager.LayerAdded += this.LayerAdded;
            layerManager.LayerRemoved += this.LayerRemoved;
            layerManager.LayerOrderChanged += this.LayerOrderChanged;
            layerManager.Invalidated += this.LayerInvalidated;
            layerManager.LayerVisibilityChanged += this.LayerVisibilityChanged;

            this.Resize += (object sender, EventArgs ea) => layoutPanel.Size = this.ClientSize - new Size(0, 20);
            this.selectedLayerPanels = new List<LayerPanel>();
        }

        private void createStatusBar()
        {
            StatusStrip statusStrip = new StatusStrip();
            statusStrip.SizingGrip = false;
            statusStrip.AutoSize = false;
            statusStrip.Height = 17;
            statusStrip.ShowItemToolTips = true;

            ToolStripButton addLayerButton = this.createStatusButton(0);
            addLayerButton.Paint += (object sender, PaintEventArgs pea) =>
            {
                pea.Graphics.FillRectangle(Brushes.Gray, 5, 1, 5, 13);
                pea.Graphics.FillRectangle(Brushes.Gray, 1, 5, 13, 5);
            };
            addLayerButton.Click += RequestAddLayer;
            statusStrip.Items.Add(addLayerButton);

            ToolStripButton removeLayerButton = this.createStatusButton(8);
            removeLayerButton.Paint += (object sender, PaintEventArgs pea) =>
            {
                Pen pen = new Pen(Brushes.Gray, 5);
                pea.Graphics.DrawLine(pen, 2, 2, 12, 12);
                pea.Graphics.DrawLine(pen, 2, 12, 12, 2);
                pen.Dispose();
            };
            removeLayerButton.Click += RequestRemoveLayer;
            statusStrip.Items.Add(removeLayerButton);

            ToolStripButton layerUpButton = this.createStatusButton(10);
            layerUpButton.Paint += (object sender, PaintEventArgs pea) =>
            {
                //draws an arrow pointing upward
                Point[] points = { new Point(7, 1), new Point(12, 7), new Point(9, 7), new Point(9, 13), new Point(5, 13), new Point(5, 7), new Point(1, 7) };
                pea.Graphics.FillPolygon(Brushes.Gray, points);
            };
            layerUpButton.Click += RequestLayerUp;
            statusStrip.Items.Add(layerUpButton);

            ToolStripButton layerDownButton = this.createStatusButton(3);
            layerDownButton.Paint += (object sender, PaintEventArgs pea) =>
            {
                //draws an arrow pointing downward
                Point[] points = { new Point(7, 13), new Point(12, 8), new Point(9, 8), new Point(9, 1), new Point(5, 1), new Point(5, 8), new Point(2, 8) };
                pea.Graphics.FillPolygon(Brushes.Gray, points);
            };
            layerDownButton.Click += RequestLayerDown;
            statusStrip.Items.Add(layerDownButton);

            ToolStripButton mergeButton = this.createStatusButton(10);
            mergeButton.Paint += (object sender, PaintEventArgs pea) =>
            {
                pea.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                pea.Graphics.FillEllipse(Brushes.Gray, 1, 2, 11, 11);
            };
            mergeButton.ToolTipText = "Merge Layers";
            mergeButton.Click += RequestMergeLayers;
            statusStrip.Items.Add(mergeButton);

            ToolStripButton explodeButton = this.createStatusButton(3);
            explodeButton.Paint += (object sender, PaintEventArgs pea) =>
            {
                pea.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                pea.Graphics.FillEllipse(Brushes.Gray, 1, 2, 5, 5);
                pea.Graphics.FillEllipse(Brushes.Gray, 8, 2, 5, 5);
                pea.Graphics.FillEllipse(Brushes.Gray, 5, 9, 5, 5);
            };
            explodeButton.Click += RequestExplodeLayer;
            explodeButton.ToolTipText = "Explode Layer";
            statusStrip.Items.Add(explodeButton);

            this.Controls.Add(statusStrip);
        }

        private ToolStripButton createStatusButton(int padding)
        {
            ToolStripButton button = new ToolStripButton();
            button.AutoSize = false;
            button.Size = new Size(15, 15);
            button.Margin = new Padding(padding, 0, 0, 0);
            return button;
        }
        #endregion

        #region eventhandlers
        private void RequestAddLayer(object sender, EventArgs ea)
        {
            if (selectedLayerPanel == null)
                layerManager.AddVisualObjectLayerAt(this.layers.Count);
            else
                layerManager.AddVisualObjectLayerAt(this.IndexOfSelected + 1);
        }

        private void RequestRemoveLayer(object sender, EventArgs ea)
        {
            if (this.SelectedLayerPanel != null)
                layerManager.RemoveLayer(this.IndexOfSelected);
        }

        private void RequestLayerUp(object sender, EventArgs ea)
        {
            if (this.SelectedLayerPanel != null)
                layerManager.MoveUpN(this.IndexOfSelected, 1);
        }

        private void RequestLayerDown(object sender, EventArgs ea)
        {
            if (this.SelectedLayerPanel != null)
                layerManager.MoveDownN(this.IndexOfSelected, 1);
        }

        private void RequestMergeLayers(object sender, EventArgs ea)
        {
            if (selectedLayerPanels.Count > 1)
                layerManager.Group(this.SelectedLayers);
        }

        private void RequestExplodeLayer(object sender, EventArgs ea)
        {
            if (this.SelectedLayerPanel != null)
                layerManager.Explode(SelectedLayerPanel.LayerSummary);
        }

        private void LayerAdded(object sender, Layers.LayerManagerEventArgs lmea)
        {
            LayerPanel lp = new LayerPanel(this, lmea.NewLayerSummary);
            this.layers.Insert(lmea.NewLayerIndex, new Tuple<LayerSummary, LayerPanel>(lmea.NewLayerSummary, lp));
            this.layoutPanel.Controls.Add(lp);
            this.rearrangeLayers();
            this.SelectedLayerPanel = lp;
        }

        private void LayerRemoved(object sender, Layers.LayerManagerEventArgs lmea)
        {
            this.layoutPanel.Controls.RemoveAt(this.layoutPanel.Controls.Count - 1 - lmea.OldLayerIndex);
            this.selectedLayerPanel = null;
            layers.RemoveAt(lmea.OldLayerIndex);
            if (this.layers.Count > 0)
                this.SelectedLayerPanel = this.layers[Math.Max(lmea.OldLayerIndex - 1, 0)].Item2;
        }

        private void LayerOrderChanged(object sender, Layers.LayerManagerEventArgs lmea)
        {
            LayerPanel lp = this.layers[lmea.OldLayerIndex].Item2;
            this.layers.RemoveAt(lmea.OldLayerIndex);
            this.layers.Insert(lmea.NewLayerIndex, new Tuple<LayerSummary, LayerPanel>(lp.LayerSummary, lp));
            rearrangeLayers();
        }

        private void LayerVisibilityChanged(object sender, Layers.LayerManagerEventArgs lmea)
        {
            LayerPanel lp = this.layers[lmea.OldLayerIndex].Item2;
            lp.LayerVisible = !lp.LayerVisible;
        }

        private void LayerInvalidated(object sender, EventArgs ea)
        {
            foreach (Tuple<LayerSummary, LayerPanel> tp in this.layers)
                tp.Item2.Invalidate();

        }
        #endregion

        #region public methods
        public void RequestChangeLayerVisibility(LayerPanel lp, bool visible)
        {
            for (int layerCount = 0; layerCount < this.layers.Count; layerCount++)
                if (this.layers[layerCount].Item2 == lp)
                {
                    layerManager.ChangeLayerVisiblity(layerCount, visible);
                    return;
                }
        }
        #endregion

        #region private methods
        private void rearrangeLayers()
        {
            for (int layerCount = 0; layerCount < this.layers.Count; layerCount++)
                this.layoutPanel.Controls.SetChildIndex(this.layers[layerCount].Item2, this.layers.Count - 1 - layerCount);
        }
        #endregion
    }
}
